#include "plugin.h"

#include <string.h>
#include <GL\glew.h>

// ----------------------------------------------------------------------------
void incrementLWMonitor(LWMonitor* monitor, BOOL handlePBuffer)
{
    if (handlePBuffer) DeactivatePBuffer();
    MON_STEP(monitor);
    if (handlePBuffer) ActivatePBuffer();
}

// ----------------------------------------------------------------------------
void strReplace(char* input, int maxLen, const char* toFind, 
                const char* toInsert)
{
    char* pPos, *inputCopy;
    int startPos, inputLen, toFindLen, toInsertLen, finalLen;
        
    pPos = strstr(input, toFind);    
    if (!pPos) return;

    startPos = (int) (pPos - input);
    toFindLen = (int) strlen(toFind);
    toInsertLen = (int) strlen(toInsert);
    inputLen = (int) strlen(input);

    finalLen = inputLen - toFindLen + toInsertLen;
    if (finalLen > maxLen)
    {
        printf("Input string to short! Given: %d; needed: %d",
               maxLen, finalLen);
        return;
    }

    inputCopy = calloc(inputLen+1, sizeof(char));
    strcpy_s(inputCopy, inputLen+1, input);

    // construct new string
    strcpy_s(input + startPos, maxLen - startPos, toInsert);
    strcpy_s(input + startPos + toInsertLen, 
             maxLen - startPos - toInsertLen, 
             inputCopy + startPos + toFindLen);

    free(inputCopy);
}

// ----------------------------------------------------------------------------
BOOL checkGLerror()
{
    GLenum errCode;
    const GLubyte* errStr;

    errCode = glGetError();
    if (errCode != GL_NO_ERROR)
    {
        errStr = gluErrorString(errCode);
        WriteTrace("An OpenGL error was encountered: %s", errStr);
        return FALSE;
    }
    else
        return TRUE;
}

// ----------------------------------------------------------------------------
void clearGLerrors()
{
    while (glGetError() != GL_NO_ERROR) {}
}

// ----------------------------------------------------------------------------
double transform(const double pt[3], MAT4 t, double tpt[3])
{
    double w;

    tpt[0] = pt[0] * t[0][0]
           + pt[1] * t[1][0]
           + pt[2] * t[2][0] + t[3][0];
    tpt[1] = pt[0] * t[0][1]
           + pt[1] * t[1][1]
           + pt[2] * t[2][1] + t[3][1];
    tpt[2] = pt[0] * t[0][2]
           + pt[1] * t[1][2]
           + pt[2] * t[2][2] + t[3][2];
    w      = pt[0] * t[0][3]
           + pt[1] * t[1][3]
           + pt[2] * t[2][3] + t[3][3];

    if (w != 0) {
        tpt[0] /= w;
        tpt[1] /= w;
        tpt[2] /= w;
    }

    return w;
}

// ----------------------------------------------------------------------------
double dot(const double a[3], const double b[3])
{
    return (a[0] * b[0] + a[1] * b[1] + a[2] * b[2]);
}

// ----------------------------------------------------------------------------
void cross(double result[3], const double a[3], const double b[3])
{
    result[0] = a[1]*b[2] - a[2]*b[1];
    result[1] = a[2]*b[0] - a[0]*b[2];
    result[2] = a[0]*b[1] - a[1]*b[0];
}

// ----------------------------------------------------------------------------
double magnitude(const double vector[3])
{
    return sqrt(vector[0] * vector[0] + 
                vector[1] * vector[1] + 
                vector[2] * vector[2]);
}

// ----------------------------------------------------------------------------
BOOL normalize(double result[3], const double v[3])
{    
    double m = magnitude(v);
    if (m != 0)
    {
        result[0] = v[0] / m;
        result[1] = v[1] / m;
        result[2] = v[2] / m;
        return TRUE;
    }
    else return FALSE;
}

// ----------------------------------------------------------------------------
double calcTriangleArea2D(const double* a, const double* b, const double* c)
{
    return 0.5 * fabs(a[0] * (c[1] - b[1]) + b[0] * (a[1] - c[1]) 
        + c[0] * (b[1] - a[1]));
}

// ----------------------------------------------------------------------------
double calcTriangleArea3D (const double* a, const double* b, const double* c) 
{
    double vecAB[3], vecAC[3], crossProd[3];                            
    
    if (a==0 || b==0 || c==0) return 0;

    subtract(vecAB, b, a);
    subtract(vecAC, c, a);
    cross(crossProd, vecAB, vecAC);

    return magnitude(crossProd) / 2.0;
}

// ----------------------------------------------------------------------------
void subtract(double result[3], const double a[3], const double b[3])
{
    result[0] = a[0] - b[0];
    result[1] = a[1] - b[1];
    result[2] = a[2] - b[2];
}

// ----------------------------------------------------------------------------
void add(double result[3], const double a[3], const double b[3])
{
    result[0] = a[0] + b[0];
    result[1] = a[1] + b[1];
    result[2] = a[2] + b[2];
}

// ----------------------------------------------------------------------------
void multiply(double result[3], const double v[3], double scalar)
{
    result[0] = v[0] * scalar;
    result[1] = v[1] * scalar;
    result[2] = v[2] * scalar;
}

// ----------------------------------------------------------------------------
BOOL isSameSide(const double p1[3], const double p2[3],
                const double a[3], const double b[3])
{
    double cp1[3], cp2[3], a_b[3], a_p1[3], a_p2[3];
    subtract(a_b, b, a);
    subtract(a_p1, p1, a);
    subtract(a_p2, p2, a);
    cross(cp1, a_b, a_p1);
    cross(cp2, a_b, a_p2);
    return dot(cp1, cp2) >= 0;
}

// ----------------------------------------------------------------------------
BOOL isPointInTriangle(const double p[3], const double a[3], 
                       const double b[3], const double c[3])
{
    // very fast way to find if point is inside triangle. Taken from here:
    // http://www.blackpawn.com/texts/pointinpoly/default.html
    
    return (isSameSide(p, a, b, c) && isSameSide(p, b, a, c) &&
        isSameSide(p, c, a, b));
}

// ----------------------------------------------------------------------------
double rad2deg(double rad)
{
    return rad / (2.0 * PI) * 360.0;
}

// ----------------------------------------------------------------------------
double deg2rad(double deg)
{
    return deg / 360.0 * 2.0 * PI;
}

// --- global lightwave functions ---------------------------------------------

const LWItemInfo* getLWItemInfo(GlobalFunc* global)
{
    return global(LWITEMINFO_GLOBAL, GFUSE_TRANSIENT);
}

const LWSceneInfo* getLWSceneInfo(GlobalFunc* global)
{
    return global(LWSCENEINFO_GLOBAL, GFUSE_TRANSIENT);
}

const LWObjectInfo* getLWObjectInfo(GlobalFunc* global)
{
    return global(LWOBJECTINFO_GLOBAL, GFUSE_TRANSIENT);
}

const LWCameraInfo* getLWCameraInfo(GlobalFunc* global)
{
    return global(LWCAMERAINFO_GLOBAL, GFUSE_TRANSIENT);
}

const LWTimeInfo* getLWTimeInfo(GlobalFunc* global)
{
    return global(LWTIMEINFO_GLOBAL, GFUSE_TRANSIENT);
}

const LWXPanelFuncs* getLWPanelFuncs(GlobalFunc* global)
{
    return global(LWXPANELFUNCS_GLOBAL, GFUSE_TRANSIENT);
}

const LWImageList* getLWImageList(GlobalFunc* global)
{
    return global(LWIMAGELIST_GLOBAL, GFUSE_TRANSIENT);
}

const char* getLWDirectory(GlobalFunc* global, const char* dirType)
{
    LWDirInfoFunc* dirInfo = global(LWDIRINFOFUNC_GLOBAL, GFUSE_TRANSIENT);
    if (dirInfo) return dirInfo(dirType);
    else return NULL;
}

void showLWMessage(GlobalFunc* global, int lwMsgType, const char* line1, const char* line2)
{
    LWMessageFuncs* msgFunc = global(LWMESSAGEFUNCS_GLOBAL, GFUSE_TRANSIENT);
    
    DeactivatePBuffer();
    
    switch (lwMsgType)
    {
    case LWMsgType_Info: 
        msgFunc->info(line1, line2);
        break;
    case LWMsgType_Warning:
        msgFunc->warning(line1, line2);
        break;
    case LWMsgType_Error:
        msgFunc->error(line1, line2);
        break;
    }
    
    ActivatePBuffer();
}